/*
 * Copyright 2005 Lumis EIP Tecnologia da Informação
 */
package lumis.portal.presentation;

import lumis.portal.AccessDeniedException;
import lumis.portal.FileUploadDeniedException;
import lumis.portal.PageNotFoundException;
import lumis.portal.PortalContext;
import lumis.portal.PortalException;
import lumis.portal.PortalObjectNotFoundException;
import lumis.portal.PortalRequestContext;
import lumis.portal.PortalRequestParameters;
import lumis.portal.PortalStringResource;
import lumis.portal.UnexpectedException;
import lumis.portal.acl.PortalPermissions;
import lumis.portal.authentication.AccessError;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.channel.ChannelConfig;
import lumis.portal.channel.ChannelWebResource;
import lumis.portal.controller.ControllerException;
import lumis.portal.controller.ControllerHtml;
import lumis.portal.el.HttpServletRequestVariableResolver;
import lumis.portal.el.LumisExpressionEvaluatorImpl;
import lumis.portal.el.PortalFunctionMapper;
import lumis.portal.file.FileParameter;
import lumis.portal.group.GroupConfig;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.manager.ManagerFactoryInternal;
import lumis.portal.monitor.IMeasureType;
import lumis.portal.monitor.IMonitor;
import lumis.portal.monitor.MonitorUtil;
import lumis.portal.page.PageConfig;
import lumis.portal.page.PageWebResource;
import lumis.portal.page.acl.PagePermissions;
import lumis.portal.page.link.PageLinkConfig;
import lumis.portal.page.webresource.PageWebResourceData;
import lumis.portal.presentation.core.IPageRenderer;
import lumis.portal.presentation.core.LayoutFileDispatcher;
import lumis.portal.presentation.core.LayoutFilePage;
import lumis.portal.presentation.layout.defaultlayout.GenericMarkupResourceStreamProvider;
import lumis.portal.presentation.layout.defaultlayout.LayoutFileMarkupResourceStreamProvider;
import lumis.portal.presentation.perspective.IPortalMode;
import lumis.portal.presentation.perspective.Perspective;
import lumis.portal.presentation.perspective.admin.IPortalStudioPageRenderer;
import lumis.portal.presentation.perspective.admin.PortalStudioMarkupResourceStreamProvider;
import lumis.portal.servicecontainer.ServiceContainer;
import lumis.portal.servicecontainer.ServiceContainerActionRequest;
import lumis.portal.servicecontainer.ServiceContainerActionResponse;
import lumis.portal.servlet.BufferedHttpServletResponse;
import lumis.portal.servlet.ServletUtil;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.portal.url.WebResource;
import lumis.portal.url.WebResourceDiscoveryFilter;
import lumis.portal.url.IURLManager.CreateStrategy;
import lumis.util.CookieUtil;
import lumis.util.FileUtil;
import lumis.util.IResource;
import lumis.util.ITransaction;
import lumis.util.PortalUtil;
import lumis.util.TextUtil;
import lumis.util.converter.DateTimeConverter;
import lumis.util.log.ILogger;
import lumis.util.log.INavigationLogger;
import lumis.util.log.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.FunctionMapper;

import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.markup.IMarkupResourceStreamProvider;

/**
 * Entry class for page rendering. Called by main.jsp.
 *
 * @version $Revision: 13319 $ $Date: 2011-08-05 18:10:18 -0300 (Fri, 05 Aug 2011) $
 * @since 4.0.0
 */
public class PresentationControllerHtml extends ControllerHtml
{
	public static class DataPreviewMode
	{
		public boolean previewModeEnabled = false;
		public boolean stopPreviewModeEnabled = true;
	}
	
	/**
	 * {@link IPageRenderer} implementation used by {@link PresentationControllerHtml}.
	 *
	 * @version $Revision: 13319 $ $Date: 2011-08-05 18:10:18 -0300 (Fri, 05 Aug 2011) $
	 * @since 5.6.0
	 */
	protected class PresentationPageRenderer implements IPageRenderer
	{
		/**
		 * The EL evaluator used by {@link #evaluateEL(String)}.
		 * @since 5.6.0
		 */
		private ExpressionEvaluator evaluator = new LumisExpressionEvaluatorImpl();
		
		/**
		 * Used to localize strings.
		 * @since 5.6.0
		 */
		private FunctionMapper portalFunctionMapper = new PortalFunctionMapper();
		
		/**
		 * Cache key value used by {@link #getCacheKey(MarkupContainer, Class)}
		 * when no custom layout file is to be used.
		 * @since 5.6.0
		 */
		private final String genericLayoutFileCacheKey = getClass().getName() + "#genericLayoutFileMarkup";
		
		public CharSequence evaluateEL(String expression)
		{
			try
			{
				return (String)evaluator.evaluate(expression, String.class, new HttpServletRequestVariableResolver(request)
				{
					@Override
					public Object resolveVariable(String variableName) throws ELException
					{
						if ("pageWebResource".equals(variableName))
							return webResource;
						return super.resolveVariable(variableName);
					}
				}, portalFunctionMapper);
			}
			catch (ELException e)
			{
				logger.error("Error evaluating expression: " + expression, e);
				return null;
			}
		}
		
		public String getRootChannelId() 
		{
			return rootChannelId;
		}

		public DataPreviewMode getDataPreviewMode()	
		{
			return dataPreviewMode;
		}

		public String getExecutedProcessActionServiceInterfaceInstanceId() 
		{
			return executedProcessActionServiceInterfaceInstanceId;
		}

		public Map<String, String[]> getMultiPartFormParameters() 
		{
			return multiPartFormParameters;
		}

		public String getMultiPartFormServiceInterfaceInstance() 
		{
			return multiPartFormServiceInterfaceInstance;
		}

		public PortalRequestParameters getParameters() 
		{
			return parameters;
		}

		public String getPreviousParametersXmlString() 
		{
			return previousParametersXmlString;
		}

		public PageWebResource getWebResource()
		{
			return PresentationControllerHtml.this.webResource;
		}

		public IMarkupResourceStreamProvider getMarkupResourceStreamProvider(LayoutFilePage layoutFilePage)
		{
			if(layoutFilePage.getLayoutFilePath() != null)
				return new LayoutFileMarkupResourceStreamProvider(layoutFilePage.getLayoutFilePath(), portalMode);
			try
			{
				return new GenericMarkupResourceStreamProvider(ManagerFactory.getPageManager().getLayoutType(sessionConfig, pageConfig, PortalTransactionFactory.getCurrentTransaction()) == PageConfig.PAGE_LAYOUT_TYPE_TABLELESS, portalMode);
			}
			catch (PortalException e)
			{
				throw new RuntimeException(e);
			}
		}

		public String getMarkupCacheKey(LayoutFilePage layoutFilePage)
		{
			String layoutFilePath = layoutFilePage.getLayoutFilePath();
			if (layoutFilePath != null)
			{
				return getClass().getName() + "#" + PortalModeUtil.getPortalMode(request).getPerspective().value() + "#" + layoutFilePath;
			}
			else
			{
				String layoutype;
				try 
				{
					layoutype = ((ManagerFactory.getPageManager().getLayoutType(sessionConfig, pageConfig, PortalTransactionFactory.getCurrentTransaction()) == PageConfig.PAGE_LAYOUT_TYPE_TABLELESS)?"tableless":"table");
				} 
				catch (PortalException e) 
				{
					throw new RuntimeException(e);
				}
				return genericLayoutFileCacheKey + "#" + layoutype + "#" + PortalModeUtil.getPortalMode(request).getPerspective().value();
			}
		}
	}
	
	private class PortalStudioPageRenderer extends PresentationPageRenderer implements IPortalStudioPageRenderer
	{
		public boolean isSafeMode()
		{
			return isSafeRenderMode;
		}
		
		@Override
		public IMarkupResourceStreamProvider getMarkupResourceStreamProvider(LayoutFilePage layoutFilePage)
		{
			if (isSafeMode() && layoutFilePage.getLayoutFilePath() != null)
				return new PortalStudioMarkupResourceStreamProvider("safeModeLayoutFileMarkup.html", portalMode);
			else if (layoutFilePage.getLayoutFilePath() != null)
				return new PortalStudioMarkupResourceStreamProvider(super.getMarkupResourceStreamProvider(layoutFilePage), portalMode);
			else
				return new PortalStudioMarkupResourceStreamProvider("genericLayoutFileMarkup.html", portalMode);
		}
		

		@Override
		public String getMarkupCacheKey(LayoutFilePage layoutFilePage)
		{
			return super.getMarkupCacheKey(layoutFilePage) + ";sm=" + isSafeMode();
		}
	}

	private static ILogger logger = LoggerFactory.getLogger(PresentationControllerHtml.class);
	protected static INavigationLogger navigationLogger = LoggerFactory.getNavigationLogger();

	/**
	 * The name of the attribute where the original URI should be stored when
	 * performing a dispatch to this controller.
	 * @since 5.0.0
	 */
	static final String ATTRIBUTE_ORIGINAL_URI = "lumis.portal.presentation.PresentationControllerHtml#ORIGINAL_URI";
	
	protected String browserInfo = null;
	protected PageConfig pageConfig = null;
	protected PageWebResource webResource;
	protected ChannelConfig channelConfig = null;
	protected IResource resource = new PortalStringResource();
	protected ServiceContainer serviceContainer;
	protected IPortalMode portalMode = Perspective.USER.getModes().iterator().next();
	protected boolean isSafeRenderMode = false;
	protected PortalRequestParameters parameters = null;
	protected String previousParametersXmlString = null;
	protected String splitterPosition = "234";
	protected String navigationPaneHidden = "false";
	protected String processActionRedirectDestination = null;
	protected boolean isPrinting = false;
	protected Map<String, String[]> multiPartFormParameters = null;
	protected Map<String, FileParameter> multiPartFormFileParameters = null;
	protected String multiPartFormServiceInterfaceInstance = null;
	protected String uploadDir = null;
	protected List<String> parentChannelIds;
	protected String rootChannelId;
	protected DataPreviewMode dataPreviewMode = new DataPreviewMode();
	protected String highlightInterfaceInst = null;
	ChannelConfig portalAdminCategoryChannelConfig;
	protected PageWebResourceData pageWebResourceData = null;
	protected IPageRenderer pageRenderer;
	
	/**
	 * The identifier of the service interface instance that was the target of
	 * a process action executed in the current request.
	 * @since 5.5.0
	 */
	protected String executedProcessActionServiceInterfaceInstanceId;
	
	/**
	 * The file where the raw request input content is stored.
	 * @since 5.5.0
	 */
	protected File rawRequestInputFile;

	public PresentationControllerHtml(HttpServletRequest request, HttpServletResponse response, PageContext pageContext) throws ControllerException, PortalException
	{
		super(request, response);
	}

	/**
	 * Renders a portal page. Based on the logged on user, pageId passed and
	 * channelId passed in the request query string, this method calculates and
	 * renders the appropriate page. This method also checks if there is a
	 * ProcessAction to be handled before rendering the page. Based on the page
	 * mode, this method either calls {@link #renderPageAdminMode}or
	 * {@link #renderPage}
	 *
	 * @throws ControllerException
	 * @throws PortalException
	 */
	public void renderPage() throws ControllerException, PortalException
	{
		try
		{
			ITransaction portalTransaction = PortalTransactionFactory.createTransaction();
			try
			{
				portalTransaction.begin();

				loadUserInfo(portalTransaction);
				setBrowserInfo();
				checkForFileUpload(portalTransaction);
				setWebResource(portalTransaction);
				setPageMode(portalTransaction);
				setLocale();
				setPageInfo(portalTransaction);
				setDataPreviewMode(portalTransaction);
				
				if (Perspective.ADMIN.equals(portalMode.getPerspective()) || this.dataPreviewMode.previewModeEnabled)
					resource.addResourcePath("lumis/portal/strings/adminstrings");

				if (checkForProtocolRedirect(portalTransaction))
					return;

				if (request.getParameter(PortalRequestParameters.PAGE_PARAMETER_PRINTING) != null && request.getParameter(PortalRequestParameters.PAGE_PARAMETER_PRINTING).equals("1"))
					this.isPrinting = true;

				if (this.pageConfig != null)
				{
					if (Perspective.USER.equals(portalMode.getPerspective()))
						navigationLogger.logAccess(request, sessionConfig, pageConfig, portalTransaction);

					parameters = new PortalRequestParameters(sessionConfig, request, multiPartFormParameters, portalTransaction);
					processActionIfRequired(portalTransaction);
					previousParametersXmlString = parameters.generatePreviousParameters(request).getValue();

					// check for redirect. continue rendering only if no
					// redirect destination was specified.
					if (processActionRedirectDestination == null)
					{
						prepareForPageRendering(portalTransaction);

						switch (portalMode.getPerspective())
						{
							case ADMIN:
								renderPageAdminMode(portalTransaction);
								break;
								
							case PUBLISHER:
								renderPagePublisherMode(portalTransaction);
								break;

							default:
								renderPageUserMode(portalTransaction);
								break;
						}
						outputServerIdIfNeeded();
						outputDateTimeIfNeeded();
					}
					else
						response.sendRedirect(processActionRedirectDestination);

					if (uploadDir != null)
					{
						FileUtil.deleteDir(uploadDir);
					}
				}
				else
				{
					PageLinkConfig pageLinkConfig = ManagerFactoryInternal.getPageLinkManager().getByTypeAndWebsite(this.getSessionConfig() , PageLinkConfig.PAGE_LINK_TYPE_HOME_PAGE, PortalUtil.getWebsite(request), PortalRequestContext.getPortalRequestContext(request), portalTransaction);
					response.sendRedirect(request.getContextPath() + "/" + PageConfig.PAGE_MAIN + "?" + PortalRequestParameters.PAGE_PARAMETER_PAGEID + "=" + pageLinkConfig.getId());
				}

				portalTransaction.commit();
			}
			catch (PortalObjectNotFoundException e)
			{
				portalTransaction.rollback();
				response.sendRedirect(request.getContextPath() + "/" + PageConfig.PAGE_MAIN);
			}
			catch (PageNotFoundException e)
			{
				if (Perspective.ADMIN.equals(portalMode.getPerspective()))
					response.sendRedirect(request.getContextPath() + "/" + PageConfig.PAGE_MAIN);
				else
					throw e;
			}
			catch (AccessDeniedException e)
			{
				portalTransaction.rollback();

				String queryString = "";
				if (request.getQueryString() != null)
					queryString = "?" + request.getQueryString();

				if (Perspective.ADMIN.equals(portalMode.getPerspective()))
				{
					response.sendRedirect(request.getRequestURL().toString() + queryString);
				}
				else
				{
					String loginError = "";

					if(e.getMessage() != null && e.getMessage().length() > 0 && !"1".equals(request.getParameter("logout")))
						loginError = "?accessError=" + URLEncoder.encode(e.getMessage(), "UTF-8");

					CookieUtil.setCookie(request, response, "lumRequestedPage", request.getRequestURL().toString() + queryString);
					response.sendRedirect(request.getContextPath() + "/login.jsp" + loginError);
				}
			}
			catch (Exception e)
			{
				portalTransaction.rollback();
				throw e;
			}
			finally
			{
				portalTransaction.dispose();
			}
		}
		catch (PageNotFoundException e)
		{
			try
			{
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
			}
			catch (IOException e1)
			{
				// IOException in this case is usually due to browser closed 
				// connection. Let's just log as debug
				logger.debug("IOException when sending page not found response code", e);
			}
			return;
		}
		catch (UnexpectedException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Outputs server id in generated HTML.
	 * 
	 * @see IPresentationManager#PROPERTY_PRESENTATION_OUTPUT_SERVER_ID
	 * 
	 * @since 6.0.0
	 */
	private void outputServerIdIfNeeded() throws IOException
	{
		// if enabled in the property bag, append a comment
		// with the current server id (replacing "--" by "__").
		List<String> outputServerIdProperty = webResource.getProperties().get(IPresentationManager.PROPERTY_PRESENTATION_OUTPUT_SERVER_ID);
		if (outputServerIdProperty != null && !(outputServerIdProperty.isEmpty()) && Boolean.TRUE.toString().equals(outputServerIdProperty.get(0)))
			out.write("<!-- LumisServerId=\"" + PortalContext.getServerId().replaceAll("\\-\\-", "__") + "\" -->");
	}
	
	/**
	 * Outputs the current date and time in generated HTML.
	 * 
	 * @see IPresentationManager#PROPERTY_PRESENTATION_OUTPUT_DATE_TIME
	 * 
	 * @since 6.0.0
	 */
	private void outputDateTimeIfNeeded() throws IOException
	{
		// if enabled in the property bag, append a comment
		// with the current date time.
		List<String> outputDateTimeProperty = webResource.getProperties().get(
				IPresentationManager.PROPERTY_PRESENTATION_OUTPUT_DATE_TIME);
		if (outputDateTimeProperty != null && !(outputDateTimeProperty.isEmpty())
				&& Boolean.TRUE.toString().equals(outputDateTimeProperty.get(0)))
			out.write("<!-- LumisGenerationDateTime=\""
					+ new DateTimeConverter().convert(String.class, new Date(), sessionConfig.getLocale(),
							DateTimeConverter.PATTERN_ISO_8601) + "\" -->");
	}
	
	/**
	 * Performs any initialization required before page rendering.
	 * 
	 * Called before rendering Admin or User mode.
	 * 
	 * @param portalTransaction
	 * @throws PortalException
	 * @since 4.2.0
	 */
	protected void prepareForPageRendering(ITransaction portalTransaction) throws PortalException
	{
		if(isSafeRenderMode)
		{
			pageWebResourceData = new PageWebResourceData();
		}
		else
		{
			webResource.setPreviewModeEnabled(dataPreviewMode.previewModeEnabled);
			pageWebResourceData = webResource.getPageWebResourceData();
		}
	}

	/**
	 * Sets the Page Mode. This could either be
	 * <code>PortalContext.MODE_ADMIN</code>, or
	 * <code>PortalContext.MODE_USER</code>. 
	 * @param portalTransaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void setPageMode(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			portalMode = PortalModeUtil.getPortalMode(sessionConfig, webResource, request,
					response, portalTransaction);

			if (Perspective.USER.equals(portalMode.getPerspective()))
			{
				this.highlightInterfaceInst = request.getParameter(PortalRequestParameters.PAGE_PARAMETER_HIGHLIGHT_INTERFACE_INST);
				this.isSafeRenderMode = false;
			}
			else if (Perspective.ADMIN.equals(portalMode.getPerspective()))
			{
				ManagerFactory.getMonitorManager().getMonitorContext().setEnabled(false);
				resource.addResourcePath("lumis/portal/strings/adminstrings");

				this.isSafeRenderMode = false;
				if ( "1".equals(request.getParameter("lumSafeRenderMode")) || "1".equals(CookieUtil.getCookie(request,"lumSafeRenderMode")) )
					this.isSafeRenderMode = true;
				
				if ( "".equals(request.getParameter("lumSafeRenderMode")) && !"1".equals(CookieUtil.getCookie(request,"lumSafeRenderMode")))
					this.isSafeRenderMode = false;
			}
			else if (Perspective.PUBLISHER.equals(portalMode.getPerspective()))
			{
				ManagerFactory.getMonitorManager().getMonitorContext().setEnabled(false);
				this.isSafeRenderMode = false;
			}
			
			if(isSafeRenderMode)
			{
				if (CookieUtil.getCookie(request,"lumSafeRenderMode")==null )
					CookieUtil.setCookie(request, response, "lumSafeRenderMode", "1");
			}
			else
			{
				if (CookieUtil.getCookie(request,"lumSafeRenderMode")!=null )
					CookieUtil.deleteCookie(request, response, "lumSafeRenderMode");
			}

		}
		catch (AccessDeniedException e) 
		{
			throw e;
		}
		catch(Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Sets the Page Preview Mode. This could either be
	 * <code>true</code>, or <code>false</code>.
	 *
	 * @param portalTransaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void setDataPreviewMode(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			if("1".equals(request.getParameter(PortalRequestParameters.PAGE_PARAMETER_DATA_PREVIEW_MODE)))
			{
				if(ManagerFactory.getGroupManager().isMember(sessionConfig, GroupConfig.GROUP_PUBLISHERS_ID, sessionConfig.getUserId(), portalTransaction))
				{
					dataPreviewMode.previewModeEnabled = true;
					CookieUtil.setCookie(request, response, PortalRequestParameters.PAGE_PARAMETER_DATA_PREVIEW_MODE, "1");
				}
			}
			else if("0".equals(request.getParameter(PortalRequestParameters.PAGE_PARAMETER_DATA_PREVIEW_MODE)))
			{
				CookieUtil.deleteCookie(request, response, PortalRequestParameters.PAGE_PARAMETER_DATA_PREVIEW_MODE);
			}
			else if("1".equals(CookieUtil.getCookie(request, PortalRequestParameters.PAGE_PARAMETER_DATA_PREVIEW_MODE)))
			{
				if(ManagerFactory.getGroupManager().isMember(sessionConfig, GroupConfig.GROUP_PUBLISHERS_ID, sessionConfig.getUserId(), portalTransaction))
					dataPreviewMode.previewModeEnabled = true;
				else
					CookieUtil.deleteCookie(request, response, PortalRequestParameters.PAGE_PARAMETER_DATA_PREVIEW_MODE);
			}
			
			if(!dataPreviewMode.previewModeEnabled && request.getParameter(PortalRequestParameters.PAGE_PARAMETER_PREVIEW_ITEMID) != null)
			{
				if(ManagerFactory.getGroupManager().isMember(sessionConfig, GroupConfig.GROUP_PUBLISHERS_ID, sessionConfig.getUserId(), portalTransaction))
				{
					dataPreviewMode.previewModeEnabled = true;
					dataPreviewMode.stopPreviewModeEnabled = false;
				}
			}
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Calls WebResource#isDynamicRequestValid (that is not public and cannot be
	 * directly executed).
	 * @param request the request.
	 * @return the result.
	 * @since 6.0.0
	 */
	private boolean isWebResourceDynamicRequestValid(HttpServletRequest request)
	{
		try
		{
			Method method = WebResource.class.getDeclaredMethod("isDynamicRequestValid", HttpServletRequest.class);
			method.setAccessible(true);
			return (Boolean) method.invoke(webResource, request);
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}

	/**
	 * Render the page in UserMode.
	 *
	 * @param portalTransaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void renderPageUserMode(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		MonitorUtil.setPageAggregationValues(pageConfig, portalTransaction);
		try
		{
			// need to check for browser path redirect?
			if (!isInsideDispatch() 
					&& !this.dataPreviewMode.previewModeEnabled && !this.isPrinting
					&& !pageConfig.getId().equals(PageConfig.PAGE_ID_ADMIN_BLANK)
					&& request.getParameter(PortalRequestParameters.PAGE_PARAMETER_RUN_TIME_SERVICE_INST) == null
					&& ("GET".equals(request.getMethod()) || Perspective.USER.equals(portalMode.getPerspective()))
					&& getGeneratorPageCacheId() == null)
			{
				// if web resource's official path is not the dynamic one, redirect to it
				String webResourcePath = webResource.getPath();
				if (!webResourcePath.equals(webResource.getDynamicPath()) || !isWebResourceDynamicRequestValid(request))
				{
					response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
					response.addHeader("Location", removeLocationPortIfNecessary(request.getScheme() + "://"
							+ PortalUtil.getWebsite(request) + webResourcePath));
					return;
				}
			}
					
			// buffer the response for use later
			HttpServletResponse originalResponse = response;
			response = new BufferedHttpServletResponse(response);
			
			// monitor httpsession only if not from page cache generator
			final boolean isFromPageCacheGenerator = getGeneratorPageCacheId() != null;
			if (!isFromPageCacheGenerator)
				HttpSessionMonitor.monitorSession(sessionConfig, request.getSession());

			// start a monitor on the pageview event only if not from page cache generator
			IMonitor monitor = isFromPageCacheGenerator ? null : ManagerFactory.getMonitorManager().getMonitor(PresentationMonitorUtil.EVENT_PAGE_VIEW, null);
			if (monitor != null)
			{
				monitor.setTransaction(portalTransaction, null);
				monitor.start();
			}
			final long pageWebResourceRenderStartMillis = System.currentTimeMillis();
			final long pageWebResourceRenderEndMillis;
			try
			{
				pageRenderer = new PresentationPageRenderer();
				renderLayoutFile();
				pageWebResourceRenderEndMillis = System.currentTimeMillis();
			}
			finally
			{
				if (monitor != null)
					monitor.stop(null);
			}			
			
			// get the page output
			byte[] htmlBytes = ((BufferedHttpServletResponse)response).getOutputByteArray();
			String htmlString = new String(htmlBytes, response.getCharacterEncoding());
			
			// put originalResponse back in place and write the buffered html
			response = originalResponse;
			out.write(htmlString);

			// evaluate the html
			HtmlEvaluator htmlEvaluator = new HtmlEvaluator(webResource);
			if (htmlEvaluator.isEnabled())
			{
				htmlEvaluator.evaluate(htmlString);
			}
			
			// monitor page web resource render
			long pageSizeBytes = htmlBytes.length;
			IMonitor pageWebResourceRenderMonitor = ManagerFactory.getMonitorManager().getMonitor(PresentationMonitorUtil.EVENT_PAGE_WEB_RESOURCE_RENDER, null);
			PresentationMonitorUtil.logPageWebResourceRenderTime(pageWebResourceRenderEndMillis - pageWebResourceRenderStartMillis, pageSizeBytes);
			pageWebResourceRenderMonitor.collect(pageWebResourceRenderStartMillis, pageWebResourceRenderEndMillis, null);			
			pageWebResourceRenderMonitor.collect(IMeasureType.MEASURE_TYPE_SIZE_BYTE, pageSizeBytes, null);
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
		finally
		{
			// remove monitor context values set
			MonitorUtil.removePageAggregationValues();
		}
	}

	/**
	 * Removes the location's server port if necessary.
	 * 
	 * @param location
	 *            the location.
	 * @return the treated location.
	 * @since 6.0.0
	 */
	private String removeLocationPortIfNecessary(String location)
	{
		// remove port only if location is absolute and its protocol is http.
		if (location.toLowerCase().startsWith("http://"))
		{
			return location.replaceFirst(":80(?=/)", "");
		}
		return location;
	}

	/**
	 * Indicates if this presentation controller is being executed inside
	 * a server side dispatch (include or forward).
	 * <p>
	 * For working properly, the {@link #ATTRIBUTE_ORIGINAL_URI} attribute
	 * must be set when dispatching to this controller.
	 * @return <code>true</code> if executing inside a server side dispatch, 
	 * <code>false</code> otherwise.
	 * @see #forward(String, HttpServletRequest, HttpServletResponse)
	 * @since 5.0.0
	 */
	private boolean isInsideDispatch()
	{
		return request.getAttribute(ATTRIBUTE_ORIGINAL_URI) != null;
	}
	
	/**
	 * Forwards a request to the presentation controller.
	 * @param path the path for the forward.
	 * @param request the request.
	 * @param response the response.
	 * @see ServletRequest#getRequestDispatcher(String)
	 * @see RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
	 * @since 5.0.0
	 */
	public static void forward(String path, HttpServletRequest request, 
			HttpServletResponse response) throws ServletException, IOException
	{
		request.setAttribute(ATTRIBUTE_ORIGINAL_URI, request.getRequestURI());
		
		// In Weblogic 10.0, the request.getServletPath() changes after requestDispatcher.forward 
		// is called, so it must be stored in a local variable
		String servletPath = ServletUtil.getApplicationRequestedPath(request);
		
		// need to fix references only if servlet path is not in the web 
		// application root directory
		boolean needFixReferences = servletPath.lastIndexOf('/') > 0;
		if (needFixReferences)
		{
			BufferedHttpServletResponse responseWrapper = new BufferedHttpServletResponse(response);
			request.getRequestDispatcher(path).forward(request, responseWrapper);
			
			// update the HTML references
			String htmlContent = responseWrapper.getOutputString();
			try
			{
				htmlContent = ManagerFactoryInternal.getURLManager().fixReferencesAccordingToPath(htmlContent, servletPath);
			}
			catch (Exception e)
			{
				throw new ServletException(e);
			}
			response.getWriter().write(htmlContent);
		}
		else
		{
			request.getRequestDispatcher(path).forward(request, response);
		}
		
		request.removeAttribute(ATTRIBUTE_ORIGINAL_URI);
	}

	/**
	 * Stores the request header HTTP_USER_AGENT value in a member variable
	 *
	 * @param portalTransaction
	 */
	protected void setBrowserInfo()
	{
		browserInfo = request.getHeader("User-Agent");
		if (browserInfo == null)
			browserInfo = "";
	}

	/**
	 * Sets {@link #webResource} with the web resource instance for the
	 * current request. 
	 * @param transaction transaction for persistence access.
	 * @since 5.0.0
	 */
	protected void setWebResource(ITransaction transaction) throws PortalException
	{
		WebResource discoveredWebResource = WebResourceDiscoveryFilter.getWebResource(request);
		
		// if the web resource corresponds to a channel web resource,
		// replace it with a corresponding page web resource
		if (discoveredWebResource instanceof ChannelWebResource)
		{
			PageConfig pageConfig = ManagerFactory.getPageManager().getFirstPageByChannelIdRecursively(
					sessionConfig, ((ChannelWebResource)discoveredWebResource).getChannelId(), transaction);
			discoveredWebResource = ManagerFactoryInternal.getURLManager().getWebResourceByDynamicPath("/" + 
					PageConfig.PAGE_MAIN + "?" + PortalRequestParameters.PAGE_PARAMETER_PAGEID + "=" + pageConfig.getId(),
					CreateStrategy.AUTO, discoveredWebResource.getLocale());
		}

		if (discoveredWebResource == null)
			throw new PageNotFoundException("No web resource for request: " + ServletUtil.getApplicationRequestedPath(request));
		
		webResource = (PageWebResource) discoveredWebResource;
	}

	/**
	 * If necessary, updates the locale of the current session according to
	 * the web resource being accessed.
	 * @since 6.0.1
	 */
	protected void setLocale()
	{
		Locale webResourceLocale = webResource.getLocale();
		if (!sessionConfig.getLocale().equals(webResourceLocale))
		{
			// change session locale to web resource's locale
			sessionConfig.setLocale(webResourceLocale);
			CookieUtil.setCookie(request, response, "lumUserLocale", webResourceLocale.toString());
		}
	}

	/**
	 * This method calls the <code>presentationManager.getDefaultPage</code>
	 * to calculate the page that needs to be rendered. If the presentation
	 * manager could not calculate the page, the user is redirected to
	 * lumisadmin.jsp page. If the destination page is the framework blank page,
	 * the mode of the page is forced to PortalContext.MODE_USER since the blank
	 * page should never be modified in terms of layout etc. If runtime
	 * interface and service instance is specified, the appropriate values are
	 * added to the page config.
	 *
	 * @param portalTransaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void setPageInfo(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			pageConfig = webResource.getPage();

			this.channelConfig = ManagerFactory.getChannelManager().get(sessionConfig, pageConfig.getChannelId(), portalTransaction);
			parentChannelIds = ManagerFactory.getChannelManager().getPathIds(sessionConfig, pageConfig.getChannelId(), portalTransaction);
			rootChannelId = pageConfig.getChannelId();

			if (parentChannelIds != null && !parentChannelIds.isEmpty())
				rootChannelId = parentChannelIds.get(0);

			// validate page access, setting mode to user if user may not access admin mode
			AccessError ae = validatePageAccess(portalTransaction);

			// check if safe mode is allowed, setting it to false if not
			if (isSafeRenderMode)
			{
				if (Perspective.USER.equals(portalMode.getPerspective()) || rootChannelId.equals(ChannelConfig.LUMIS_ADMIN_CHANNEL_ID) || this.pageConfig.getType() == PageConfig.PAGE_TYPE_ADMIN)
				{
					isSafeRenderMode = false;
				}
			}
			// verify access validated above
			if (ae.getCause() != AccessError.CAUSE_NO_ERROR)
			{
				throw new AccessDeniedException(ae.serialize());
			}
		}
		catch (AccessDeniedException e)
		{
			throw e;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	protected AccessError validatePageAccess(ITransaction transaction) throws PortalException
	{
		boolean hasAccess = false;
		AccessError accessError = new AccessError();
		accessError.setCause(AccessError.CAUSE_NO_ERROR);
		
		hasAccess = ManagerFactory.getPageAclManager().checkPermission(sessionConfig, pageConfig.getId(), PagePermissions.VIEW_PAGE, transaction);

		if(!hasAccess)
			accessError.setCause(AccessError.CAUSE_PAGE_ACCESS_DENIED);

		return accessError;
	}

	protected boolean checkForProtocolRedirect(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			// 0-inherit, 1-http, 2-https and 3-all
			int pageProtocolId = ManagerFactory.getPageManager().getProtocolType(sessionConfig, pageConfig, portalTransaction);
			if (pageProtocolId == PageConfig.PAGE_PROTOCOL_ALL)
				return false;

			String currentPageProtocolScheme = null;
			if (request.getScheme() != null)
				currentPageProtocolScheme = request.getScheme();

			String pageProtocolScheme = ManagerFactory.getPageManager().getProtocolScheme(sessionConfig, pageProtocolId, portalTransaction);

			if (pageProtocolScheme != null && !currentPageProtocolScheme.toUpperCase().equals(pageProtocolScheme.toUpperCase()))
			{
				String currentServerName = "";
				if (request.getServerName() != null)
					currentServerName = request.getServerName();
				
				String port = "";
				if (PortalContext.getNonSSLConnectorPort() != null && pageProtocolScheme.toUpperCase().equals(PageConfig.PAGE_PROTOCOL_HTTP_NAME.toUpperCase()))
					port = ":"+PortalContext.getNonSSLConnectorPort();
				
				if (PortalContext.getSSLConnectorPort() != null && pageProtocolScheme.toUpperCase().equals(PageConfig.PAGE_PROTOCOL_HTTPS_NAME.toUpperCase()))
					port = ":"+PortalContext.getSSLConnectorPort();
				
				String currentRequestURI = "";
				if (request.getRequestURI() != null)
					currentRequestURI = request.getRequestURI();
					
				String currentQueryString = "";
				if (request.getQueryString() != null)
					currentQueryString = "?"+request.getQueryString();
	
				response.sendRedirect(pageProtocolScheme+"://"+currentServerName+port+currentRequestURI+currentQueryString);
				return true;
			}
			
			return false;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Checks to see if an action is to be processed. This is detected based on
	 * the page parameter <code>PageConfig.PAGE_PARAMETER_PROCESS_ACTION</code>
	 * has the value of 1. Once the ProcessAction is handled, the returned
	 * render parameters are remembered to pass on to the interface instance
	 * that initiated the process action. If no render parameters are returned,
	 * the interface instance that initiated the process action will not receive
	 * any parameters for its render method.
	 *
	 * @param portalTransaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void processActionIfRequired(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			this.serviceContainer = PortalContext.getServiceContainer();

			// Handle Process Action if any
			if (hasProcessAction())
			{
				logger.debug("Handling process action");
				ServiceContainerActionRequest actionRequest = new ServiceContainerActionRequest(sessionConfig, parameters, request);
				ServiceContainerActionResponse actionResponse = new ServiceContainerActionResponse(actionRequest, response);

				actionRequest.setSessionConfig(this.sessionConfig);
				actionRequest.setPageWebResource(webResource);
				actionResponse.setPageWebResource(webResource);
				actionRequest.setMode(portalMode.getPerspective().value());

				// find interfaceInstanceId of destination for process action
				String interfaceInstanceId = multiPartFormServiceInterfaceInstance;
				if (interfaceInstanceId == null)
				{
					interfaceInstanceId = request.getParameter(PortalRequestParameters.PAGE_PARAMETER_INTERFACE_INST);
					if (interfaceInstanceId == null)
						throw new IllegalArgumentException("STR_ACTION_MUST_BE_FOR_A_SERVICE_INTERFACE_INSTANCE");
				}
				
				actionRequest.setServiceInterfaceInstanceId(interfaceInstanceId);
				actionResponse.setServiceInterfaceInstanceId(interfaceInstanceId);
				actionRequest.setCallerServiceInterfaceInstanceId(request.getParameter(PortalRequestParameters.PAGE_PARAMETER_CALLER_INTERFACE_INST));

				if (this.multiPartFormFileParameters != null)
					actionRequest.setServiceInterfaceInstanceFileParameters(this.multiPartFormFileParameters);

				// set input stream for the request
				InputStream rawRequestInputStream = null;
				if (rawRequestInputFile != null)
				{
					rawRequestInputStream = new FileInputStream(rawRequestInputFile);
					actionRequest.setInputStream(rawRequestInputStream);
				}
				try
				{
					// execute the process action
					serviceContainer.processServiceInterfaceInstanceAction(actionRequest, actionResponse, portalTransaction);
				}
				finally
				{
					// close the input stream if it was opened
					if (rawRequestInputStream != null)
						rawRequestInputStream.close();
				}
				
				// since a process action execution may cause the current 
				// sessionConfig to change (e.g.: due to a login or logout),
				// updating the sessionConfig variable to the current sessionConfig.
				sessionConfig = SessionConfig.getCurrentSessionConfig();
				
				if (actionResponse.isRedirectSet())
					processActionRedirectDestination = actionResponse.getRedirectDestination();

				// apply process action's render parameters to parameters variable
				this.executedProcessActionServiceInterfaceInstanceId = actionRequest.getServiceInterfaceInstanceId();
				// apply service interface instance parameters
				parameters.applyServiceInterfaceInstanceParameters(actionRequest.getServiceInterfaceInstanceId(), 
						actionResponse.getRenderParameters());
			}
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	protected boolean hasProcessAction()
	{
		if (
				(request.getParameter(PortalRequestParameters.PAGE_PARAMETER_PROCESS_ACTION) != null && request.getParameter(PortalRequestParameters.PAGE_PARAMETER_PROCESS_ACTION).equals("1")) ||
				(this.multiPartFormParameters != null && this.multiPartFormParameters.containsKey(PortalRequestParameters.PAGE_PARAMETER_PROCESS_ACTION) && (this.multiPartFormParameters.get(PortalRequestParameters.PAGE_PARAMETER_PROCESS_ACTION))[0].equals("1"))
			)
			return true;
		else
			return false;
	}

	/**
	 * Renders the page in administration mode (F12). When the page is rendered
	 * in admin mode, several additional javascript files need to be included in
	 * the page. Besides the javascript, the navigationPane, workPane, splitter
	 * and header need to be rendered as well.
	 *
	 * @param portalTransaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void renderPageAdminMode(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			pageRenderer = new PortalStudioPageRenderer();
			renderLayoutFile();
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Renders the page in publisher perspective. When the page is rendered
	 * in publisher perspective, several additional javascript files need to be included in
	 * the page. Besides the javascript, the navigationPane, workPane, splitter
	 * and header need to be rendered as well.
	 *
	 * @param portalTransaction the current transaction
	 * @throws ControllerException
	 * @throws PortalException
	 */
	protected void renderPagePublisherMode(ITransaction portalTransaction) throws ControllerException, PortalException
	{
		try
		{
			pageRenderer = new PresentationPageRenderer();
			renderLayoutFile();
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}

	/**
	 * Renders the layout file for the current {@link #pageRenderer}.
	 * @throws ServletException if an exception is thrown during the layout file
	 * processing.
	 * @throws IOException if an exception is thrown during the output to the 
	 * response.
	 * @since 5.6.0
	 */
	protected void renderLayoutFile() throws ServletException, IOException
	{
		LayoutFileDispatcher layoutFileDispatcher = new LayoutFileDispatcher(pageRenderer);
		layoutFileDispatcher.forward(request, response);
	}

	/**
	 * Returns the identifier of the page cache the current request is generating.
	 * @return the page cache identifier, or <code>null</code> if the current
	 * request is not for generating a page cache.
	 * @since 5.0.0
	 */
	protected String getGeneratorPageCacheId()
	{
		return request.getParameter(PortalRequestParameters.PAGE_PARAMETER_FROM_PCM);
	}

	protected void checkForFileUpload(ITransaction transaction) throws PortalException
	{
		File folder = null;

		try
		{
			if (FileUpload.isMultipartContent(request))
			{
				String uploadId = PortalUtil.generateNewGuid();
				multiPartFormParameters = new HashMap<String, String[]>();
				multiPartFormFileParameters = new HashMap<String, FileParameter>();
				multiPartFormServiceInterfaceInstance = request.getParameter(PortalRequestParameters.PAGE_PARAMETER_INTERFACE_INST);

				// Create a new file upload handler
				DiskFileUpload upload = new DiskFileUpload();
				upload.setHeaderEncoding("UTF-8");

				// TODO define a parameter for the temporary upload folder.
				String tempDir = PortalContext.getDataPath("/temp/" + uploadId);
				folder = new File(tempDir);
				folder.mkdirs();

				// TODO define a parameter for the max size of an uploaded file
				// upload.setSizeMax();
				upload.setRepositoryPath(tempDir);
				
				// create upload storage folder
				if (uploadDir == null)
					uploadDir = PortalContext.getDataPath("/upload/" + uploadId);
				File uploadFolder = new File(uploadDir);
				uploadFolder.mkdirs();
				
				// save request input to rawRequestInputFile
				rawRequestInputFile = new File(uploadFolder, "rawRequestInput");
				FileOutputStream rawInputFileOutputStream = new FileOutputStream(rawRequestInputFile);
				try
				{
					InputStream requestInputStream = request.getInputStream();
					try
					{
						FileUtil.copyStream(requestInputStream, rawInputFileOutputStream);
					}
					finally
					{
						requestInputStream.close();
					}
				}
				finally
				{
					rawInputFileOutputStream.close();
				}

				// parse the input into items
				final FileInputStream rawInputFileInputStream = new FileInputStream(rawRequestInputFile);
				List<?> items;
				try
				{
					RequestContext requestContext = new ServletRequestContext(request)
					{
						@Override
						public InputStream getInputStream() throws IOException
						{
							return rawInputFileInputStream;
						}
					};
					items = upload.parseRequest(requestContext);
				}
				finally
				{
					rawInputFileInputStream.close();
				}
				
				Iterator<?> iter = items.iterator();
				while (iter.hasNext())
				{
					FileItem item = (FileItem) iter.next();

					if (!item.isFormField())
					{
						if (!ManagerFactory.getPortalAclManager().checkPermission(sessionConfig, PortalPermissions.FILE_UPLOAD, transaction))
							throw new FileUploadDeniedException();

						String fileFullName = item.getName().replace('\\', '/');

						Pattern p = Pattern.compile("[:\\*\\?\\|'<>#{}%&~\\t\";]");
						Matcher m = p.matcher(fileFullName);
						fileFullName = m.replaceAll("_");

						String[] fileFullNameArray = fileFullName.split("/");
						String fileName = fileFullNameArray[fileFullNameArray.length - 1];

						String contentType = item.getContentType();
						// if no content type was added to the http header, we
						// consider "application/octet-stream"
						if (contentType == null || "".equals(contentType))
							contentType = "application/octet-stream";
						long sizeInBytes = item.getSize();

						if(PortalContext.getMaxFileUploadSize() != -1)
						{
							if(sizeInBytes > PortalContext.getMaxFileUploadSize())
								throw new FileUploadDeniedException("STR_FILE_EXCEEDED_LIMIT;"+PortalContext.getMaxFileUploadSize());
						}


						if (fileName.length() > 0)
						{
							if (uploadDir == null)
								uploadDir = PortalContext.getDataPath("/upload/" + uploadId);

							String curUploadDir = uploadDir + "/" + PortalUtil.generateNewGuid();
							String fileFullPath = curUploadDir + "/" + fileName;
							try
							{
								File folderUpload = new File(curUploadDir);
								folderUpload.mkdirs();
								File uploadedFile = new File(fileFullPath);

								item.write(uploadedFile);

								addMultiPartFormParameter(item.getFieldName(), fileFullPath);
								FileParameter fileParameter = new FileParameter(uploadedFile.getCanonicalPath(), contentType, sizeInBytes);
								this.multiPartFormFileParameters.put(item.getFieldName(), fileParameter);
							}
							catch (Exception e)
							{
								throw new PortalException("STR_FILE_WRITE_EXCEPTION;" + TextUtil.escapeLocalizationParameter(fileFullPath), e);
							}
						}
					}
					else
					{
						if (item.getSize() > 0)
						{
							try
							{
								String parameterValue = item.getString("UTF-8");
								String parameterName = item.getFieldName();
								
								if (multiPartFormServiceInterfaceInstance == null && PortalRequestParameters.PAGE_PARAMETER_INTERFACE_INST.equals(parameterName))
									multiPartFormServiceInterfaceInstance = parameterValue;
								
								addMultiPartFormParameter(parameterName, parameterValue);
							}
							catch (UnsupportedEncodingException e)
							{
								throw new UnexpectedException(e);
							}
						}
					}
				}

				folder.delete();
			}
		}
		catch (IOException e)
		{
			throw new PortalException("STR_FILE_UPLOAD_EXCEPTION", e);
		}
		catch (FileUploadException e)
		{
			throw new PortalException("STR_FILE_UPLOAD_EXCEPTION", e);
		}
		finally
		{
			// remove temp dir
			if (folder != null)
				folder.delete();
		}
	}
	
	/**
	 * Adds a multipart form parameter.
	 * 
	 * This method checks if a parameter with the same name was previously included.
	 * If so, the new parameter value is appended to the end of the values array. 
	 * 
	 * @param parameterName The name of the parameter.
	 * @param parameterValue The value of the parameter.
	 * @since 4.2.0
	 */
	protected void addMultiPartFormParameter(String parameterName, String parameterValue)
	{
		if (parameterValue.length() > 0 && parameterName.length() > 0)
		{
			String prevParameterValueArray[] = multiPartFormParameters.get(parameterName);
			if(prevParameterValueArray != null)
			{
				String parameterValueArray[] = new String[prevParameterValueArray.length+1];
				System.arraycopy(prevParameterValueArray, 0, parameterValueArray, 0, prevParameterValueArray.length);
				parameterValueArray[parameterValueArray.length-1] = parameterValue;
				this.multiPartFormParameters.put(parameterName, parameterValueArray);
			}
			else
			{
				String parameterValueArray[] = new String[1];
				parameterValueArray[0] = parameterValue;
				this.multiPartFormParameters.put(parameterName, parameterValueArray);
			}
		}
	}
}
